﻿//#define DEBUG_SHOW_CREATION
//#define DEBUG_SHOW_CREATION_PRINT_2ND_TRI


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Drawing; //for Color class


namespace DirectX_CS
{
	public class Cartesian3dSurface : IManagedBuffers, IManagedMaterial, IRangeModifiable, IManagedModelTransformable, IDisposable
	{
		[PGBrowsable]
		public Range3d Range
		{
			get { return new Range3d(XMin, YMin, ZMin, XMax, YMax, ZMax); } 
		}
		float mZMin = -10, mZMax = 10; //should be maximum and mimum of the function though... in the future.
		public float ZMin
		{
			get { return mZMin; }
			set { mZMin = value; }
		}
		public float ZMax
		{
			get { return mZMax; }
			set { mZMax = value; }
		}
		public float YMax
		{
			get { return dataCreator.YMax; }
			set { dataCreator.YMax = value; }
		}

		public float YMin
		{
			get { return dataCreator.YMin; }
			set { dataCreator.YMin = value; }
		}

		public float XMax
		{
			get { return dataCreator.XMax; }
			set { dataCreator.XMax = value; }
		}

		public float XMin
		{
			get { return dataCreator.XMin; }
			set { dataCreator.XMin = value; }
		}
		public int DivisionsY
		{
			get { return dataCreator.DivisionsY; }
			set { dataCreator.DivisionsY = value; }
		}

		public int DivisionsX
		{
			get { return dataCreator.DivisionsX; }
			set { dataCreator.DivisionsX = value; }
		}
		public double Time
		{
			get { return dataCreator.Time; }
			set { dataCreator.Time = value; }
		}
		public ZFuncOfXY CartZofXY
		{
			get { return dataCreator.ZFunc; }
			set { dataCreator.ZFunc = value; }
		}
		public event VBChangedDelegate VertexBufferChanged;
		public event IBChangedDelegate IndexBufferChanged;

		//public delegate float CartesianZFunc(float x, float y);

		protected bool mEnableCallRenderPrimitives = true;
		public bool EnableCallRenderPrimitives
		{
			get { return mEnableCallRenderPrimitives; }
			set { mEnableCallRenderPrimitives = value; }
		}
		protected bool mEnableCallRenderIndexedPrimitives = false;
		public bool EnableCallRenderIndexedPrimitives
		{
			get { return mEnableCallRenderIndexedPrimitives; }
			set { mEnableCallRenderIndexedPrimitives = value; }
		}

		public void Recreate()
		{
			dataCreator.CreateVertexData();
			CreateIndexData();
			//DebugShowDataIndices();
			VertexBufferChanged(this, dataCreator.GetVerticesCount); //as soon as its registered calls will probably start arriving.
			IndexBufferChanged(this, mNumIndices);	
		}
		public void UpdateTransform()
		{
			VertexBufferChanged(this, dataCreator.GetVerticesCount);
		}
        protected CartesianDataCreator dataCreator = null;
		public VertexFormats VertexFormat
		{
			get { return SVector.Format; }
		}

        public CartesianDataCreator DataCreator
        {
            get { return dataCreator; }
            set { dataCreator = value;
                VertexBufferChanged(this, dataCreator.GetVerticesCount); //as soon as its registered calls will probably start arriving.
                IndexBufferChanged(this, mNumIndices);    
            }
        }

		protected PrimitiveManager renderer = null; //main memory manager..change name.
		protected Device mDevice = null;

		protected int[] mTerrainIndices = null;
		bool mSeeThrough = false;

		protected int mNumIndices = 0;
        Color mMaterialColor = Color.Red;

		protected Material mMaterial;
        public Material Material
        {
            get {return mMaterial;}
			set { mMaterial = value; }			
        }
		public int GetVerticesCount()
		{
			return dataCreator.GetVerticesCount;
		}
		public int GetIndicesCount()
		{
			return mNumIndices;
		}

		protected Vector3 position;

		public Cartesian3dSurface(Device device, PrimitiveManager rendManager)
		{
			Initialize(device, rendManager, CartesianDataCreator.xyZFuncPlaneXY, null);
		}
		public Cartesian3dSurface(Device device, PrimitiveManager rendManager, Cartesian3dFunction matchDataObject)
		{
            Initialize(device, rendManager, null, matchDataObject.DataCreator);
		}
        public Cartesian3dSurface(Device device, PrimitiveManager rendManager, CartesianDataCreator dataCreator)
        {
            Initialize(device, rendManager, null, dataCreator);
        }
        private void Initialize(Device device, PrimitiveManager rendManager, ZFuncOfXY ZofXY, CartesianDataCreator dCreator)
        {
			position = new Vector3(0, 0, 0);
			if (dCreator == null)
				dataCreator = new CartesianDataCreator(ZofXY);
			else
				dataCreator = dCreator;
			
            if (ZofXY == null)
                throw new Exception("ZofXY cannot equal null!");
                   
            renderer = rendManager;
            mDevice = device; //set before registering for sure...

    		//protected Material mMaterial;

			mMaterial = new Material();
			mMaterial.Specular = Color.FromArgb(122,255,255,255);
            mMaterial.Ambient = Color.FromArgb(122,255,255,255);
            mMaterial.Diffuse = Color.FromArgb(122, 255,255,255);

            //theZFuncDataCreator.DebugShowDataIndexMap();

			dataCreator.CreateVertexData();
            CreateIndexData();
            //DebugShowDataIndices();


			PrimitiveManager.Register(this);
			VertexBufferChanged(this, dataCreator.GetVerticesCount); //as soon as its registered calls will probably start arriving.
			IndexBufferChanged(this, mNumIndices);

        }
		~Cartesian3dSurface()
		{
			DisposeInstance();
		}

		public void DisposeInstance()
		{
			if (renderer != null)
				renderer.UnregisterPrimitive(this); //cannot hurt to unregister.
		}
		public bool SeeThrough
		{
			get { return mSeeThrough; }
            set
            {
                mSeeThrough = value;
            }
		}

		public Array GetVertices() //was SVector[]
		{
			return (Array)dataCreator.GetVertices();
		}
		public void RenderPrimitives(PrimitiveManager caller, int dataLength)
		{
			mDevice.DrawPrimitives(PrimitiveType.PointList, 0, dataLength); //was dataLength-1);
		}
		/// <summary>
		/// Since each primitiveWithVertices should for diversity be in charge of its own representation
		/// in rendering, although the memory will be managed.
		/// </summary>
		public void RenderIndexedPrimitives(PrimitiveManager caller, int vbDataLength, int ibLength)
        {
            int expectedPrimitiveCount = ibLength / 3;
            int primDifferenceWasted = expectedPrimitiveCount - PrimitiveCount;

			mDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vbDataLength, 0,  PrimitiveCount);
		}

		public int NumberFaces
		{
			get { return mTerrainIndices.Length / 3; }
		}	

		private TransformMatrix vTransform = new TransformMatrix();

		public TransformMatrix ModelView
		{
			get { return vTransform; }
			set { vTransform = value; }
		}
	    
		public void ManagerDestroyed(PrimitiveManager caller)
		{
			//signals to registered primitiveWithVertices generator that renderer is or has been destroyed
			//so we should stop sending messages or at least relaying them to the renderer
			renderer = null;
		}
		/// <summary>
		/// End of required implementation;
		/// </summary>
		/// 

		/// <summary>
		/// Computes mTerrainVertices and mTerrainIndices
		/// </summary>
		/// 
		int PrimitiveCount
		{       
            get {
                return mNumIndices / 3;
            }
		}						  
		int IndicesCount
		{
			get { return mTerrainIndices.Length; }
		}

		public Array GetIndices() //was int[]
		{
			return (Array)mTerrainIndices;
		}
		private void CreateIndexData()
		{
			bool seeThrough = mSeeThrough;
            int indicesCount = ( (mSeeThrough == true)? 1 : 2) * 3 * ((dataCreator.DivisionsX ) * dataCreator.DivisionsY);
			//we will not recreate the array we will use every data point because
			//there is at least one point in the data set that is used at least once
			if (indicesCount <= 0) throw new Exception("Invalid num of verts. Cannot create terrain mesh-data with 0 crossectional area.");

			mTerrainIndices = new int[indicesCount];	//int numTerrainIndices = 3 * primitiveCount;

			//int vertexIdx = 0;
			int terrainIdx = 0;
            int maxIndexY = dataCreator.DivisionsY - 1;
            int maxIndexX = dataCreator.DivisionsX - 1;
            for (int row = 1; row < dataCreator.DivisionsY + 1; row++)  //row and col are row and column index of the vertex in question
			{
                for (int col = 0; col < dataCreator.DivisionsX ; col++) //dont go to the last row or column we need our index to work in i - 1 notation.
				{
				#if DEBUG_SHOW_CREATION
					//SEE DATA POINT we are dealing with. 
					//int vertexIdx = theZFuncDataCreator.VertexIndexFromRC(row, col);
                    System.Diagnostics.Debug.Write(string.Format("cr[{0},{1}]", col, row));
                    //System.Diagnostics.Debug.WriteLine(string.Format("i[{0}] ", vertexIdx));
				    System.Diagnostics.Debug.WriteLine(string.Format("t.Start[{0}]  ", terrainIdx));	
                #endif
                    SetTerrainIndices(row, col, TriangleType.ABOVE_RIGHT, terrainIdx);
                #if DEBUG_SHOW_CREATION
                    //print_triangle(terrainIdx);
                    print_triangle_points(terrainIdx, true);
                    System.Diagnostics.Debug.WriteLine("");
                #endif
					terrainIdx += 3;

					if (seeThrough == false)
					{
		
						SetTerrainIndices(row, col, TriangleType.RIGHT_ABOVE, terrainIdx);
						terrainIdx += 3;
					}

                #if DEBUG_SHOW_CREATION_PRINT_2ND_TRI
                    //print_triangle(terrainIdx);
                    print_triangle_points(terrainIdx, true);
                    System.Diagnostics.Debug.WriteLine("");
                    //NOTE: SHOULD SET THE SURFACE NORMALS
                #endif

                #if DEBUG_SHOW_CREATION
					//System.Diagnostics.Debug.WriteLine(string.Format("tIdx.End = {0}  ", terrainIdx -1));
                #endif

				}//end for col			
                #if DEBUG_SHOW_CREATION
                    System.Diagnostics.Debug.WriteLine("");
                #endif
			} //end for row

            mNumIndices = terrainIdx - 3;
		}


        //int VertexCount
        //{
        //    get { return theZFuncDataCreator.VertexCount; }
        //}

		private int[] GetTriangle(short row, short col)
		{
			//int row, int col
            int indexNum = dataCreator.VertexIndexFromRC(row, col); //get the index
			return GetTriangle(indexNum);
		}

		private int [] GetTriangle(int terrainStartIndex)
		{
			int[] links = new int[3];
            links[0] = mTerrainIndices[terrainStartIndex];
            links[1] = mTerrainIndices[terrainStartIndex + 1];
            links[2] = mTerrainIndices[terrainStartIndex + 2];
			return links;
		}

        



		public enum TriangleType
		{
			ABOVE_LEFT,
			ABOVE_RIGHT,
			BELOW_LEFT,
			BELOW_RIGHT,
			RIGHT_ABOVE,
		}
		//offset for v2 and v3:
		// v2yo v2xo  v3yo  v3x0 (row comes first, then column)
		short[] ABOVE_LEFT = { 0, -1, -1, -1 };
		short[] BELOW_RIGHT = { 1, 0, 1, 1 }; //seems to work good was originally what i did at the origin.
		short[] ABOVE_RIGHT = { -1, 0, -1, 1 };
		short[] BELOW_LEFT = { 1, 0, 1, -1 };

		short[] RIGHT_ABOVE = { -1, 1, 0, 1};

		public short VertexIndexFromRC(int row, int col)
		{
			return dataCreator.VertexIndexFromRC(row, col);
		}

		private void Debug_ShowTerrainTriangleAt(short customVertexIndex)
		{
			short terrainIndex = (short)(customVertexIndex * 3);

			string msg = string.Format("[{0} {1} {2}] ",
				mTerrainIndices[terrainIndex],
				mTerrainIndices[terrainIndex + 1],
				mTerrainIndices[terrainIndex + 2]);

			System.Diagnostics.Debug.WriteLine(msg);
			System.Diagnostics.Debug.Flush();
		}

		private void SetTerrainIndices
		(int v1RowIndex, int v1ColIndex,
			TriangleType type, int terrainIdx
		)
		{
			//get 2 POINT offsets from triangle type.
			short v2RowOffset = 0;
			short v2ColOffset = 0;
			short v3RowOffset = 0;
			short v3ColOffset = 0;

			switch (type)
			{
				case TriangleType.ABOVE_LEFT:
					v2RowOffset = ABOVE_LEFT[0];
					v2ColOffset = ABOVE_LEFT[1];
					v3RowOffset = ABOVE_LEFT[2];
					v3ColOffset = ABOVE_LEFT[3];
					break;
				case TriangleType.ABOVE_RIGHT:
					v2RowOffset = ABOVE_RIGHT[0];
					v2ColOffset = ABOVE_RIGHT[1];
					v3RowOffset = ABOVE_RIGHT[2];
					v3ColOffset = ABOVE_RIGHT[3];
					break;
				case TriangleType.RIGHT_ABOVE:
					v2RowOffset = RIGHT_ABOVE[0];
					v2ColOffset = RIGHT_ABOVE[1];
					v3RowOffset = RIGHT_ABOVE[2];
					v3ColOffset = RIGHT_ABOVE[3];
					break;
				case TriangleType.BELOW_LEFT:
					v2RowOffset = BELOW_LEFT[0];
					v2ColOffset = BELOW_LEFT[1];
					v3RowOffset = BELOW_LEFT[2];
					v3ColOffset = BELOW_LEFT[3];
					break;
				case TriangleType.BELOW_RIGHT:
					v2RowOffset = BELOW_RIGHT[0];
					v2ColOffset = BELOW_RIGHT[1];
					v3RowOffset = BELOW_RIGHT[2];
					v3ColOffset = BELOW_RIGHT[3];
					break;
			}

            short r1 = (short) v1RowIndex;
            short c1 = (short) v1ColIndex;
			short r2 = (short)(v1RowIndex + v2RowOffset);
			short c2 = (short)(v1ColIndex + v2ColOffset);
            short aidx2 = VertexIndexFromRC(r2, c2);

			short r3 = (short)(v1RowIndex + v3RowOffset);
			short c3 = (short)(v1ColIndex + v3ColOffset);
            short aidx3 = VertexIndexFromRC(r3, c3);

            short aidx1 = VertexIndexFromRC(v1RowIndex, v1ColIndex);
			short len = (short)(mTerrainIndices.Length); //do not make an index past this number

			//now check bounds
			if (aidx1 < len && aidx2 < len && aidx3 < len)
			{

				//do first side of square	      //0 * 3 = 0, 1*3 = 3, 2*3 =6
				//0     1     2     3       4
				System.Diagnostics.Debug.Assert(terrainIdx + 2 < mTerrainIndices.Length);
                mTerrainIndices[terrainIdx] = aidx3;
                mTerrainIndices[terrainIdx + 1] = aidx2;
                mTerrainIndices[terrainIdx + 2] = aidx1;

            #if DEBUG_SHOW_CREATION
                System.Diagnostics.Debug.WriteLine("cr[" + c1 + "," + r1 + "] = i[" + aidx1 + "]");
                System.Diagnostics.Debug.WriteLine("cr[" + c2 + "," + r2 + "] = i[" + aidx2 + "]");
                System.Diagnostics.Debug.WriteLine("cr[" + c3 + "," + r3 + "] = i[" + aidx3 + "]");
            #endif
			}
		}

        private void DebugShowDataIndices()
        {
            System.Diagnostics.Debug.WriteLine("mTerrainIndices data indices by triangle (ordered) list follows...");
            int dataRow = 0;
            int dataCol = 0;
            int evalsX = dataCreator.DivisionsX + 1;
            for (int tIdx = 0; tIdx<  mTerrainIndices.Length; tIdx += 3)
            {

                System.Diagnostics.Debug.Write(mTerrainIndices[tIdx].ToString() + " ");
                System.Diagnostics.Debug.Write(mTerrainIndices[tIdx+1].ToString() + " ");
                System.Diagnostics.Debug.Write(mTerrainIndices[tIdx+2].ToString() + "   ");

                if (tIdx % 6 == 0 && tIdx != 0) //if a pair of terrain "triangles" has passed
                    System.Diagnostics.Debug.Write("   ");


                if (tIdx % dataCreator.DivisionsX == 0 && tIdx != 0) //if idx on next row
                {
                    dataRow++;   //next column move down and to the left.
                    dataCol = 0; //reset column index (to the left)
                    System.Diagnostics.Debug.WriteLine("> Data Index New Row " + dataRow.ToString() + "<");
                }
                else
                {
                    dataCol++;
                }

            }
            /*
            System.Diagnostics.Debug.Write(">> ");

            for (short col = 0; col <= DivisionsX; col++)
            {
                System.Diagnostics.Debug.Indent();
                System.Diagnostics.Debug.Write(col.ToString() + "\t\t\t");
            }
            System.Diagnostics.Debug.IndentLevel = 0;

            System.Diagnostics.Debug.WriteLine("");
            System.Diagnostics.Debug.WriteLine(">>>>>>>>");

            for (short j = 0; j <= DivisionsY - 1; j++)
            {
                System.Diagnostics.Debug.Write((j*(DivisionsX + 1)).ToString() + "> ");
                for (short i = 0; i <= DivisionsX; i++)
                {
                    short index = DataIndexFromRC(j, i);
                    print_triangle(j, i);
                }
                System.Diagnostics.Debug.IndentLevel = 0;
                System.Diagnostics.Debug.WriteLine("");
            }
            System.Diagnostics.Debug.Flush();
             */
        }

        private void print_triangle(int TerrainStartIndex)
        {
            int[] tri = GetTriangle(TerrainStartIndex);
            System.Diagnostics.Debug.Write(" << ");
            //System.Diagnostics.Debug.Indent(); 
            System.Diagnostics.Debug.Write(tri[0].ToString() + " < ");
            //System.Diagnostics.Debug.Indent(); 
            System.Diagnostics.Debug.Write(tri[1].ToString() + " < ");
            //System.Diagnostics.Debug.Indent();
            System.Diagnostics.Debug.Write(tri[2].ToString() + " \t");
        }
		//private void print_triangle_points(int TerrainStartIndex, bool printIndices)
		//{
		//    int[] tri = GetTriangle(TerrainStartIndex);
		//    if (printIndices) System.Diagnostics.Debug.Write("i[" + tri[2] + "]");
		//    System.Diagnostics.Debug.Write(m()[tri[2]].ToString() + " > ");
		//    if (printIndices) System.Diagnostics.Debug.Write("i[" + tri[1].ToString() + "]");
		//    System.Diagnostics.Debug.Write(GetVertices()[tri[1]].ToString() + " > " );
		//    if (printIndices) System.Diagnostics.Debug.Write("i[" + tri[0] + "]");
		//    System.Diagnostics.Debug.Write(GetVertices()[tri[0]].ToString() + " >> ");
		//}


	}//end of class..
}//end of namespace.

